// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @file OwnerPtr.ipp
 */

#pragma once

#include <cpp_utils/exception/ValueAccessException.hpp>

namespace eprosima {
namespace utils {

////////////////////////////
// PROTECTED CONSTRUCTOR
////////////////////////////

template<typename T>
LesseePtr<T>::LesseePtr(
        std::shared_ptr<InternalPtrData<T>> data_reference) noexcept
    : data_reference_(data_reference)
{
}

///////////////////////
// ACCESS DATA METHODS
///////////////////////

template<typename T>
T* LesseePtr<T>::operator ->() const
{
    return this->lock_with_exception().operator ->();
}

template<typename T>
T& LesseePtr<T>::operator *() const
{
    return this->lock_with_exception().operator *();
}

template<typename T>
GuardedPtr<T> LesseePtr<T>::lock() const noexcept
{
    if (data_reference_)
    {
        // Lock mutex
        data_reference_->lock_shared();
        // From here, the validity cannot change until releasing lock
    }

    // Create a GuardedPtr that will unlock mutex in destruction
    // It could be that data is not valid, and this GuardedPtr will be invalid, but still exist
    // with a valid shared_ptr to an empty data.
    // If data_reference_ is nullptr, Guarded will not be valid and so it will not unlock mutex.
    return GuardedPtr<T> (
        data_reference_);
}

template<typename T>
GuardedPtr<T> LesseePtr<T>::lock_with_exception() const
{
    auto guarded = lock();
    if (!guarded)
    {
        // Internal mutex of guarded ptr would be released when guarded
        // object exits scope (after throw).
        throw ValueAccessException(
                  "Trying to access a data not available anymore.");
    }
    else
    {
        return guarded;
    }
}

template<typename T>
LesseePtr<T>::operator bool() const noexcept
{
    return data_reference_ && data_reference_->operator bool();
}

} /* namespace utils */
} /* namespace eprosima */
